/*
 * 2_smp_synchronization.c
 * 演示多核的同步
 */

/* C头文件，支持标准C库, 可以跨平台 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#include <tests_lib.h>
#include <io.h>

/*
 * 本测例引用了SMP机制, <smp.h>中定义了SMP相关的接口,
 */
#include <smp.h>

typedef struct {
    float op1;
    float op2;
    float result;
} calc_t;

/* 
 * 这是一个同步标记点
 */
static cpu_synctoken_t smp_sync = {.ready = false};

static void multiply(void *arg)
{
    calc_t *mul = (calc_t *)arg;

    mul->result = mul->op1 * mul->op2;

    cpu_sync_barrier(&smp_sync);
}

static void divide(void *arg)
{
    calc_t *div = (calc_t *)arg;

    div->result = div->op1 / div->op2;

    cpu_sync_barrier(&smp_sync);
}

static int smp_sync_run(void)
{
    float expect1, expect2, final_expect;

    /*
     * 初始化同步标记点, 本测例中有3个CPU, 第二个参数设置为3
     */
    cpu_sync_init(&smp_sync, 3);

    calc_t mul = {3.1415926, 3.0, 0};
    expect1 = mul.op1 * mul.op2;

    calc_t div = {888.888, 3.14, 0};
    expect2 = div.op1 / div.op2;

    final_expect = expect1 + expect2;

    /* 让CPU1 CPU2跑业务,且不等待 */
    smp_call_function(1, multiply, (void *)&mul, false);
    smp_call_function(2, divide, (void *)&div, false);

    /*
     * 在同步标记处等待，上面cpu_sync_init()第二个参数设置了3
     * 因此在3个CPU都执行完cpu_sync_barrier(&smp_sync)之前,
     * 谁也不能继续执行
     */
    cpu_sync_barrier(&smp_sync);

    ASSERT_EQ(final_expect, (mul.result + div.result));

    return 0;
}

DECLARE_TEST_CASE(          /* 声明了一个单元测例, 测试框架会依次调用下面的init(), run(), exit()函数 */
        smp_sync,           /* 测例名字，不用带引号"" */
        NULL,               /* 测例预初始化函数，若不需要可以为NULL. 比如可以把复位和时钟初始化放这里 */
        smp_sync_run,       /* 测例执行主体函数，必须定义 */
        NULL                /* 测例退出函数，若不需要可以为NULL. 比如结束测例后需要关时钟 */
);
